// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#include "mk_header.h"
#include "mk_skeleton.h"
#include "mk_skeleton_data.h"
#include "mk_engine.h"
#include "mk_resource_manager.h"
#include "mk_error.h"

namespace MK3DE
{
	CSkeleton::CSkeleton()
	{
		_skeletonData = NULL;
	}

	CSkeleton::~CSkeleton()
	{
		std::vector<CBone*>::iterator i = _vecBone.begin();
		while (i != _vecBone.end())
		{
			CBone* bone = *i;
			SAFE_DELETE(bone);

			++i;
		}

		_vecBone.clear();
		SAFE_RELEASE(_skeletonData);
	}

	BOOL CSkeleton::__CreateBones()
	{
		BOOL ret = FALSE;
		for (DWORD i = 0; i < _skeletonData->GetBoneCount(); ++i)
		{
			CBone* pBone = NULL;
			std::string name;

			pBone = new CBone(this, i);
			MK_PROCESS_ERROR(pBone);

			ret = _skeletonData->GetBoneName(i, name);
			MK_PROCESS_ERROR(ret);

			pBone->SetName(name.c_str());

			_vecBone.push_back(pBone);
		}

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CSkeleton::LoadFromFile(const char* fileName)
	{
		// Load skeleton data resource.
		BOOL ret = GetEngine()->GetResourceManager()->GetSkeletonData(fileName, _skeletonData);
		MK_PROCESS_ERROR(ret);

		// Create all bones.
		ret = __CreateBones();
		MK_PROCESS_ERROR(ret);

		_frameInterpolator.SetParam(0, (float)(_skeletonData->GetFrameCount() - 1), 100.0f, IC_LOOP);
		SetTime(0);

		ret = TRUE;
Exit0:
		return ret;
	}

	void CSkeleton::CalculateBaseTransformation(CSkeleton* pSkeleton)
	{
		if (pSkeleton)
		{
			DWORD boneCount = pSkeleton->GetSkeletonData()->GetBoneCount();
			assert(boneCount == GetBoneCount());
			for (DWORD i = 0; i < boneCount; ++i)
			{
				CBone* bone1 = pSkeleton->GetBone(i);
				CBone* bone2 = GetBone(i);
				bone2->SetBaseTransform(bone1->GetBaseTransform());
			}
		}
		else
		{
			std::vector<CBone*>::iterator i = _vecBone.begin();
			while (i != _vecBone.end())
			{
				CBone* pBone = *i;
				D3DXMATRIX matBaseTransform;
				D3DXMatrixInverse(&matBaseTransform, NULL, &pBone->GetAbsoluteTransformation());
				pBone->SetBaseTransform(matBaseTransform);

				++i;
			}
		}
	}

	void CSkeleton::Update(DWORD dt, BOOL bChanged)
	{
		ASSERT(_vecBone.size());

		_frameInterpolator.Update(dt);
		float frame = _frameInterpolator.GetCurrent();
		DWORD start = (DWORD)frame;
		DWORD end = start + 1;
		float factor = frame - (float)start;
		CSceneNode* parent = _vecBone[0]->GetParent();

		std::vector<CBone*>::iterator i = _vecBone.begin();
		while (i != _vecBone.end())
		{
			CBone* pBone = (CBone*)*i;
			pBone->Update(dt, parent, bChanged, _skeletonData->IsRelative(), start, end, factor);

			++i;
		}
	}

	void CSkeleton::SetTime(float time)
	{
		_frameInterpolator.SetCurrent(time);
		Update(0, TRUE);
	}

	void CSkeleton::SetInterval(float time)
	{
		_frameInterpolator.SetInterval(time);
	}

	CBone* CSkeleton::GetBone(int BoneIdx)
	{
		if (BoneIdx >= 0 && BoneIdx < (int)_vecBone.size())
			return _vecBone[BoneIdx];

		return NULL;
	}
}